using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using AntEater.ComponentModel;
using AntEater.ComponentModel.BuildElements;
using System.Reflection;
using System.Resources;
using System.Collections.Generic;
using System.Globalization;

namespace AntEater.Runtime.UI
{
    /// <summary>
    /// View for editing Build Files.
    /// </summary>
    [Icon(typeof(BuildFileView),"AntEater.Runtime.Resources.AntEater.ico",Key="Default")]
    public partial class BuildFileView : UserControl, IView
    {

        /// <summary>
        /// Occurs when a file shoud be opened in a specific view.
        /// </summary>
        public event EventHandler<BuildFileViewEventArgs> OpenFile;

        /// <summary>
        /// Initializes a new instance of the <see cref="BuildFileView"/> class.
        /// </summary>
        public BuildFileView() {
            InitializeComponent();

            this._buildElementImageList = this.CreateTreeImageList();
            this.buildFileTreeView.ImageList = this._buildElementImageList;

            this._editBuffer = new EditBuffer();

            this.messageView.MessageClick += new EventHandler<MessageClickEventArgs>(MessageViewMessageClick);
        }

        /// <summary>
        /// Gets the build element image key.
        /// </summary>
        /// <param name="buildElement">The build element.</param>
        /// <returns></returns>
        private string GetBuildElementImageKey(IBuildElement buildElement) {
            string key = buildElement.GetType().FullName;

            if (!this.BuildElementImageList.Images.ContainsKey(key)) {
                IconAttribute iconAttribute = Utilities.GetTypeAttributeFromObject<IconAttribute>(buildElement);

                if (iconAttribute != null) {
                    Image icon = iconAttribute.Icon;

                    if (icon != null) {
                        this.BuildElementImageList.Images.Add(key, icon);
                    }
                    else {
                        key = "Default";
                    }
                }
                else {
                    key = "Default";
                }
            }

            return key;
        }

        private ImageList _buildElementImageList;

        /// <summary>
        /// Gets or sets the build element image list.
        /// </summary>
        /// <value>The build element image list.</value>
        public ImageList BuildElementImageList {
            get { return _buildElementImageList; }
            set { _buildElementImageList = value; }
        }


        private BuildFile _buildFile;

        /// <summary>
        /// Gets or sets the build file.
        /// </summary>
        /// <value>The build file.</value>
        public BuildFile BuildFile {
            get { return _buildFile; }
            set {
                if (_buildFile != null && this.BuildFile.Definition != null) {
                    _buildFile.Definition.Changed -= new EventHandler<EventArgs>(BuildFileDefinitionChanged);
                }
                _buildFile = value;
                this.messageView.Messages.Clear();
                this.messageView.Messages.AddRange(_buildFile.Messages);
                if (_buildFile != null && this.BuildFile.Definition != null) {
                    _buildFile.Definition.Changed += new EventHandler<EventArgs>(BuildFileDefinitionChanged);
                }

                this.Refresh();
            }
        }

        private bool _suppressRefresh;

        /// <summary>
        /// Gets or sets a value indicating whether [suppress refresh].
        /// </summary>
        /// <value><c>true</c> if [suppress refresh]; otherwise, <c>false</c>.</value>
        public bool SuppressRefresh {
            get { return _suppressRefresh; }
            set { _suppressRefresh = value; }
        }

        /// <summary>
        /// Gets the surface.
        /// </summary>
        /// <value>The surface.</value>
        public Control Surface {
            get { return this; }
        }

        private EditBuffer _editBuffer;

        /// <summary>
        /// Gets or sets the edit buffer.
        /// </summary>
        /// <value>The edit buffer.</value>
        public EditBuffer EditBuffer {
            get { return _editBuffer; }
            set { _editBuffer = value; }
        }


        /// <summary>
        /// Refreshes this instance.
        /// </summary>
        public override void Refresh() {
            RefreshBuildFileTreeView();
            base.Refresh();
        }

        /// <summary>
        /// Called when [open file].
        /// </summary>
        /// <param name="buildFilePath">The build file path.</param>
        /// <param name="view">The view.</param>
        protected void OnOpenFile(string buildFilePath,BuildFileViewType view) {
            if (this.OpenFile != null) {
                this.OpenFile(this,new BuildFileViewEventArgs(buildFilePath,view));
            }
        }

        /// <summary>
        /// Creates the tree image list.
        /// </summary>
        /// <returns></returns>
        private ImageList CreateTreeImageList() {
            ImageList imageList = new ImageList();

            imageList.ColorDepth = ColorDepth.Depth32Bit;
            imageList.ImageSize = new Size(16, 16);

            IconAttribute[] iconAttributes = Utilities.GetTypeAttributesFromObject<IconAttribute>(this);

            foreach (IconAttribute iconAttribute in iconAttributes) {
                Image defaultImage = iconAttribute.Icon;

                if (defaultImage != null) {
                    imageList.Images.Add(iconAttribute.Key, defaultImage);
                }
            }

            return imageList;
        }

        /// <summary>
        /// Refreshes the build file tree view.
        /// </summary>
        private void RefreshBuildFileTreeView() {
            if (!this.SuppressRefresh) {

                this.SuppressRefresh = true;

                this.buildFileTreeView.Nodes.Clear();

                if (this.BuildFile != null && this.BuildFile.Definition != null) {

                    TreeNodeImageKeyResolver imageKeyResolver =new TreeNodeImageKeyResolver(this.GetBuildElementImageKey);
                    
                    BuildElementTreeNode rootNode = this.CreateTreeNode(this.BuildFile.Definition.RootBuildElement,imageKeyResolver,true);

                    this.buildFileTreeView.Nodes.Add(rootNode);
                }

                this.SuppressRefresh = false;
            }
        }

        /// <summary>
        /// Creates the tree node.
        /// </summary>
        /// <param name="buildElement">The build element.</param>
        /// <param name="imageKeyResolver">The image key resolver.</param>
        /// <param name="createChildNodes">if set to <c>true</c> [create child nodes].</param>
        /// <returns></returns>
        private BuildElementTreeNode CreateTreeNode(IBuildElement buildElement, TreeNodeImageKeyResolver imageKeyResolver, bool createChildNodes) {
            BuildElementTreeNode node = null;

            if (buildElement != null) {

                //Create Node Object
                node = new BuildElementTreeNode(buildElement, imageKeyResolver);

                //Create Context Menu Strip
                BuildElementContextMenuStrip contextMenuStrip = new BuildElementContextMenuStrip(buildElement,this.EditBuffer);
                contextMenuStrip.OpenFile += new EventHandler<BuildFileViewEventArgs>(BuildElementContextMenuStripOpenFile);
                contextMenuStrip.AddBuildElement += new EventHandler<AddBuildElementEventArgs>(BuildElementContextMenuStripAddBuildElement);
                contextMenuStrip.CutBuildElement += new EventHandler<EventArgs>(BuildElementContextMenuStripCutBuildElement);
                contextMenuStrip.CopyBuildElement += new EventHandler<EventArgs>(BuildElementContextMenuStripCopyBuildElement);
                contextMenuStrip.PasteBuildElement += new EventHandler<PasteBuildElementEventArgs>(BuildElementContextMenuStripPasteBuildElement);
                contextMenuStrip.DeleteBuildElement += new EventHandler<EventArgs>(BuildElementContextMenuStripDeleteBuildElement);
                node.ContextMenuStrip = contextMenuStrip;

                IContainerBuildElement container = buildElement as IContainerBuildElement;

                //Check weather to create child elements or not
                if (createChildNodes && container != null) {
                    //Listen for recreate events
                    node.NeedRecreateChildNodes += new EventHandler<EventArgs>(NodeNeedRecreateChildNodes);

                    node.Nodes.AddRange(this.CreateChildNodes(node,imageKeyResolver,createChildNodes).ToArray());
                }

                //Expand the node
                node.Expand();
            }

            return node;
        }

        /// <summary>
        /// Creates the child nodes.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="imageKeyResolver">The image key resolver.</param>
        /// <param name="createChildNodes">if set to <c>true</c> [create child nodes].</param>
        /// <returns></returns>
        private List<BuildElementTreeNode> CreateChildNodes(BuildElementTreeNode node, TreeNodeImageKeyResolver imageKeyResolver, bool createChildNodes) {
            List<BuildElementTreeNode> childNodes = new List<BuildElementTreeNode>();
            IContainerBuildElement container = node.BuildElement as IContainerBuildElement;

            if (container != null) {
                foreach (IBuildElement childBuildElement in container.ChildElements) {
                    BuildElementTreeNode childNode = this.CreateTreeNode(childBuildElement, imageKeyResolver, createChildNodes);

                    childNodes.Add(childNode);
                }
            }

            return childNodes;
        }

        /// <summary>
        /// Nodes the need recreate child nodes.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void NodeNeedRecreateChildNodes(object sender, EventArgs e) {
            BuildElementTreeNode node = sender as BuildElementTreeNode;

            if (node != null) {

                IContainerBuildElement container = node.BuildElement as IContainerBuildElement;

                if (container != null) {
                    
                    //Remove old nodes
                    foreach (BuildElementTreeNode childNode in node.Nodes) {

                        if (!container.ChildElements.Contains(childNode.BuildElement)) {
                            childNode.Remove();
                        }
                    }
                    
                    //Add new nodes
                    int lastIndex = -1;
                    foreach (IBuildElement childElement in container.ChildElements) {
                        TreeNode[] childNodes = node.Nodes.Find(childElement.GetHashCode().ToString(CultureInfo.CurrentCulture), false);

                        if (childNodes.Length == 0) {
                            BuildElementTreeNode newNode = this.CreateTreeNode(childElement, node.ImageKeyResolver, true);

                            node.Nodes.Insert(lastIndex + 1, newNode);
                        }
                        lastIndex++;
                    }
                }
            }
        }

        /// <summary>
        /// Builds the element context menu strip add build element.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:AntEater.Runtime.UI.AddBuildElementEventArgs"/> instance containing the event data.</param>
        private void BuildElementContextMenuStripAddBuildElement(object sender, AddBuildElementEventArgs e) {

            IBuildElement newElement = e.ContainerBuildElement.Context.CreateNewBuildElement(e.BuildElementType);
            
            switch (e.Action) {
                case AddAction.AddChild:
                    e.ContainerBuildElement.ChildElements.Append(newElement);
                    break;
                case AddAction.AddBeforeReference:
                    e.ContainerBuildElement.ChildElements.InsertBefore(newElement, e.RefrenceBuildElement);
                    break;
                case AddAction.AddAfterReference:
                    e.ContainerBuildElement.ChildElements.InsertAfter(newElement, e.RefrenceBuildElement);
                    break;
            }

            this.SelectBuildElement(newElement);
        }

        /// <summary>
        /// Builds the element context menu strip paste build element.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:AntEater.Runtime.UI.PasteBuildElementEventArgs"/> instance containing the event data.</param>
        private void BuildElementContextMenuStripPasteBuildElement(object sender, PasteBuildElementEventArgs e) {

            if (!this.EditBuffer.IsEmpty) {

                IBuildElement buildElement = null;

                //Get build element according the edit action
                //Copy -> Clone element
                //Cut -> Move Element
                switch (this.EditBuffer.EditAction) {
                    case EditAction.Copy:
                        buildElement = this.EditBuffer.BuildElement.Clone() as IBuildElement;
                        break;
                    case EditAction.Cut:
                        buildElement = this.EditBuffer.BuildElement;
                        break;

                }

                if (buildElement != null) {

                    switch (e.Action) {
                        case PasteAction.PasteChild:
                            e.ContainerBuildElement.ChildElements.Append(buildElement);
                            break;
                        case PasteAction.PasteBeforeReference:
                            e.ContainerBuildElement.ChildElements.InsertBefore(buildElement, e.RefrenceBuildElement);
                            break;
                        case PasteAction.PasteAfterReference:
                            e.ContainerBuildElement.ChildElements.InsertAfter(buildElement, e.RefrenceBuildElement);
                            break;
                    }

                    this.SelectBuildElement(buildElement);
                }
            }
        }

        /// <summary>
        /// Builds the element context menu strip copy build element.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void BuildElementContextMenuStripCopyBuildElement(object sender, EventArgs e) {
            BuildElementContextMenuStrip contextMenuStrip = sender as BuildElementContextMenuStrip;

            if (contextMenuStrip != null) {
                IBuildElement buildElement = contextMenuStrip.BuildElement;

                this.EditBuffer.BuildElement = buildElement;
                this.EditBuffer.EditAction = EditAction.Copy;
            }
        }

        /// <summary>
        /// Builds the element context menu strip cut build element.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void BuildElementContextMenuStripCutBuildElement(object sender, EventArgs e) {
            BuildElementContextMenuStrip contextMenuStrip = sender as BuildElementContextMenuStrip;

            if (contextMenuStrip != null) {
                IBuildElement buildElement = contextMenuStrip.BuildElement;

                this.EditBuffer.BuildElement = buildElement;
                this.EditBuffer.EditAction = EditAction.Cut;
            }
        }

        /// <summary>
        /// Builds the element context menu strip show code.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void BuildElementContextMenuStripShowCode(object sender, EventArgs e) {
            
            BuildElementContextMenuStrip contextMenuStrip = sender as BuildElementContextMenuStrip;

            if (contextMenuStrip != null) {
                IBuildElement buildElement = contextMenuStrip.BuildElement;

                this.OnOpenFile(buildElement.Root.BuildFilePath, BuildFileViewType.Xml);
            }
        }

        /// <summary>
        /// Builds the element context menu strip open file.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:AntEater.Runtime.UI.BuildFileViewEventArgs"/> instance containing the event data.</param>
        private void BuildElementContextMenuStripOpenFile(object sender, BuildFileViewEventArgs e) {
            this.OnOpenFile(e.BuildFilePath, e.View);
        }

        /// <summary>
        /// Builds the element context menu strip delete build element.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void BuildElementContextMenuStripDeleteBuildElement(object sender, EventArgs e) {
            BuildElementContextMenuStrip contextMenuStrip = sender as BuildElementContextMenuStrip;

            if (contextMenuStrip != null) {
                IBuildElement buildElement = contextMenuStrip.BuildElement;
                IBuildElement parent = buildElement.Parent;

                buildElement.Context.DeleteBuildElement(buildElement);

                if (parent != null) {
                    this.SelectBuildElement(parent);
                }
            }
        }

        /// <summary>
        /// Shows the build element properties.
        /// </summary>
        /// <param name="element">The element.</param>
        private void ShowBuildElementProperties(IBuildElement element) {
            buildElementPropertyView.SelectedBuildElement = element;
        }

        /// <summary>
        /// Selects the build element.
        /// </summary>
        /// <param name="element">The element.</param>
        private void SelectBuildElement(IBuildElement element) {
            TreeNode[] nodes
                = this.buildFileTreeView.Nodes.Find(element.GetHashCode().ToString(CultureInfo.CurrentCulture), true);

            if (nodes.Length > 0) {
                this.buildFileTreeView.SelectedNode = nodes[0];
                nodes[0].EnsureVisible();
            }
        }

        /// <summary>
        /// Called when an Build Element was selected in the tree.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.Windows.Forms.TreeViewEventArgs"/> instance containing the event data.</param>
        private void TreeBuildElementSelected(object sender, TreeViewEventArgs e) {
            BuildElementTreeNode node = e.Node as BuildElementTreeNode;

            if (node != null) {
                ShowBuildElementProperties(node.BuildElement);
            }
        }

        /// <summary>
        /// Handles the Message Click Event of the MessageView.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:AntEater.Runtime.UI.MessageClickEventArgs"/> instance containing the event data.</param>
        private void MessageViewMessageClick(object sender, MessageClickEventArgs e) {
            this.SelectBuildElement(e.Message.BuildElement);
        }

        /// <summary>
        /// Handles the Changed Event of the BuildFileDefinition.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
        private void BuildFileDefinitionChanged(object sender, EventArgs e) {
            //this.Refresh();
        }

        /// <summary>
        /// Handles the ItemDrag Event of the BuildFileTreeView.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.Windows.Forms.ItemDragEventArgs"/> instance containing the event data.</param>
        private void BuildFileTreeViewItemDrag(object sender, ItemDragEventArgs e) {
            this.DoDragDrop(e.Item, DragDropEffects.Move);
        }

        /// <summary>
        /// Handles the DragEnter Event of the BuildFileTreeView.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.Windows.Forms.DragEventArgs"/> instance containing the event data.</param>
        private void BuildFileTreeViewDragEnter(object sender, DragEventArgs e) {
            if (e.Data.GetDataPresent(typeof(BuildElementTreeNode))) {
                e.Effect = e.AllowedEffect;
            }
            else {
                e.Effect = DragDropEffects.None;
            }
        }

        /// <summary>
        /// Handles the DragOver Event of the BuildFileTreeView.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.Windows.Forms.DragEventArgs"/> instance containing the event data.</param>
        private void BuildFileTreeViewDragOver(object sender, DragEventArgs e) {

            this.buildFileTreeView.Refresh();

            if (e.Data.GetDataPresent(typeof(BuildElementTreeNode))) {

                // Retrieve the client coordinates of the mouse position.
                Point targetPoint = this.buildFileTreeView.PointToClient(new Point(e.X, e.Y));

                //Get the node that is being dragged
                BuildElementTreeNode draggingNode = e.Data.GetData(typeof(BuildElementTreeNode)) as BuildElementTreeNode;

                //Get the node the mouse is pointing at
                BuildElementTreeNode targetNode = this.buildFileTreeView.GetNodeAt(targetPoint.X, targetPoint.Y) as BuildElementTreeNode;

                if (draggingNode != null && targetNode != null) {

                    // Provide scrolling
                    Rectangle treeRectangle = this.buildFileTreeView.Bounds;
                    int targetOffset = treeRectangle.Height - targetPoint.Y;

                    //Scroll down
                    if ((targetOffset < treeRectangle.Height / 2) && (targetOffset > 0)) {

                        if (targetNode.NextVisibleNode != null) {
                            targetNode.NextVisibleNode.EnsureVisible();
                        }

                    }

                    //Scroll up
                    if ((targetOffset > treeRectangle.Height / 2) && (targetOffset < treeRectangle.Height)) {

                        if (targetNode.PrevVisibleNode != null) {
                            targetNode.PrevVisibleNode.EnsureVisible();
                        }

                    }

                    //Cannot drag a node onto it self
                    if (targetNode != draggingNode) {

                        //Cannot drag a node onto one of its childnodes
                        if (targetNode.Nodes.Find(draggingNode.GetHashCode().ToString(CultureInfo.CurrentCulture), true).Length == 0) {

                            IContainerBuildElement targetContainer = null;

                            Rectangle targetNodeRectangle = targetNode.Bounds;

                            //1: Top 1/3
                            //2: Middle 1/3
                            //3: Bottom 1/3
                            int targetNodeRegion = -1;

                            //Determine the region of the tagetNode
                            if (targetPoint.Y < targetNodeRectangle.Y + (targetNodeRectangle.Height / 3)) {
                                //Top 1/3 -> Container = Parent, insert before target node
                                targetNodeRegion = 1;
                                targetContainer = targetNode.BuildElement.Parent as IContainerBuildElement;
                            }
                            else if (targetPoint.Y < targetNodeRectangle.Y + ((targetNodeRectangle.Height / 3) * 2)) {
                                //Middle 1/3 -> Container = Target Node, append child
                                targetNodeRegion = 2;
                                targetContainer = targetNode.BuildElement as IContainerBuildElement;
                            }
                            else {
                                //Bottom 1/3 -> Container = Parent, insert after target node
                                targetNodeRegion = 3;
                                targetContainer = targetNode.BuildElement.Parent as IContainerBuildElement;
                            }

                            //Check if dragged build element can be inserted into the target container
                            if (targetContainer.ChildElements.Contains(draggingNode.BuildElement) || targetContainer.CanInsert(draggingNode.BuildElement.GetType())) {

                                using (Graphics g = this.buildFileTreeView.CreateGraphics()) {

                                    Image arrowLeft = dragAndDropImageList.Images["ArrowLeft"];
                                    Image arrowRight = dragAndDropImageList.Images["ArrowRight"];

                                    if (targetNodeRegion == 1) {
                                        g.DrawLine(new Pen(Color.Blue, 3), new Point(targetNodeRectangle.Left, targetNodeRectangle.Top), new Point(targetNodeRectangle.Right, targetNodeRectangle.Top));
                                        g.DrawImage(arrowRight, new Point(targetNodeRectangle.Right, targetNodeRectangle.Top - (arrowLeft.Height / 2)));
                                        g.DrawImage(arrowLeft, new Point(targetNodeRectangle.Left - arrowRight.Width, targetNodeRectangle.Top - (arrowLeft.Height / 2)));
                                    }
                                    else if (targetNodeRegion == 2) {
                                        g.DrawLine(new Pen(Color.Blue, 3), new Point(targetNodeRectangle.Left, targetNodeRectangle.Top + ((targetNodeRectangle.Height / 2))), new Point(targetNodeRectangle.Right, targetNodeRectangle.Top + ((targetNodeRectangle.Height / 2))));
                                        g.DrawImage(arrowRight, new Point(targetNodeRectangle.Right, targetNodeRectangle.Top + ((targetNodeRectangle.Height / 2)) - (arrowLeft.Height / 2)));
                                        g.DrawImage(arrowLeft, new Point(targetNodeRectangle.Left - arrowRight.Width, targetNodeRectangle.Top + ((targetNodeRectangle.Height / 2)) - (arrowLeft.Height / 2)));

                                    }
                                    else if (targetNodeRegion == 3) {
                                        g.DrawLine(new Pen(Color.Blue, 3), new Point(targetNodeRectangle.Left, targetNodeRectangle.Bottom), new Point(targetNodeRectangle.Right, targetNodeRectangle.Bottom));
                                        g.DrawImage(arrowRight, new Point(targetNodeRectangle.Right, targetNodeRectangle.Bottom - (arrowLeft.Height / 2)));
                                        g.DrawImage(arrowLeft, new Point(targetNodeRectangle.Left - arrowRight.Width, targetNodeRectangle.Bottom - (arrowLeft.Height / 2)));
                                    }

                                }

                                e.Effect = e.AllowedEffect;
                            }
                            else {
                                e.Effect = DragDropEffects.None;
                            }
                        }
                        else {
                            e.Effect = DragDropEffects.None;
                        }
                    }
                    else {
                        e.Effect = DragDropEffects.None;
                    }
                }
                else {
                    e.Effect = DragDropEffects.None;
                }
            }
            else {
                e.Effect = DragDropEffects.None;
            }
        }

        /// <summary>
        /// Handles the DragDrop Event of the BuildFileTreeView.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.Windows.Forms.DragEventArgs"/> instance containing the event data.</param>
        private void BuildFileTreeViewDragDrop(object sender, DragEventArgs e) {

            this.buildFileTreeView.Refresh();

            if (e.Data.GetDataPresent(typeof(BuildElementTreeNode))) {

                // Retrieve the client coordinates of the mouse position.
                Point targetPoint = this.buildFileTreeView.PointToClient(new Point(e.X, e.Y));

                //Get the node that is being dragged
                BuildElementTreeNode draggingNode = e.Data.GetData(typeof(BuildElementTreeNode)) as BuildElementTreeNode;

                //Get the node the mouse is pointing at
                BuildElementTreeNode targetNode = this.buildFileTreeView.GetNodeAt(targetPoint.X, targetPoint.Y) as BuildElementTreeNode;

                if (draggingNode != null && targetNode != null) {

                    //Cannot drag a node onto it self
                    if (targetNode != draggingNode) {

                        //Cannot drag a node onto one of its childnodes
                        if (targetNode.Nodes.Find(draggingNode.GetHashCode().ToString(CultureInfo.CurrentCulture), true).Length == 0) {

                            IContainerBuildElement targetContainer = null;

                            Rectangle targetNodeRectangle = targetNode.Bounds;

                            //Determine the region of the tagetNode
                            if (targetPoint.Y < targetNodeRectangle.Y + (targetNodeRectangle.Height / 3)) {
                                //Top 1/3 -> Container = Parent, insert before target node
                                targetContainer = targetNode.BuildElement.Parent as IContainerBuildElement;

                                if (targetContainer.ChildElements.Contains(draggingNode.BuildElement) || targetContainer.CanInsert(draggingNode.BuildElement.GetType())) {
                                    targetContainer.ChildElements.InsertBefore(draggingNode.BuildElement, targetNode.BuildElement);
                                }
                            }
                            else if (targetPoint.Y < targetNodeRectangle.Y + ((targetNodeRectangle.Height / 3) * 2)) {
                                //Middle 1/3 -> Container = Target Node, append child
                                targetContainer = targetNode.BuildElement as IContainerBuildElement;

                                if (targetContainer.ChildElements.Contains(draggingNode.BuildElement) || targetContainer.CanInsert(draggingNode.BuildElement.GetType())) {
                                    targetContainer.ChildElements.Append(draggingNode.BuildElement);
                                }
                            }
                            else {
                                //Bottom 1/3 -> Container = Parent, insert after target node
                                targetContainer = targetNode.BuildElement.Parent as IContainerBuildElement;

                                if (targetContainer.ChildElements.Contains(draggingNode.BuildElement) || targetContainer.CanInsert(draggingNode.BuildElement.GetType())) {
                                    targetContainer.ChildElements.InsertAfter(draggingNode.BuildElement, targetNode.BuildElement);
                                }
                            }

                            e.Effect = e.AllowedEffect;
                        }
                        else {
                            e.Effect = DragDropEffects.None;
                        }
                    }
                    else {
                        e.Effect = DragDropEffects.None;
                    }

                    //Select the Dragged Build Element
                    this.SelectBuildElement(draggingNode.BuildElement);
                }
                else {
                    e.Effect = DragDropEffects.None;
                }
            }
            else {
                e.Effect = DragDropEffects.None;
            }
        }

        /// <summary>
        /// Builds the file tree view mouse clicked.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="T:System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void BuildFileTreeViewMouseClicked(object sender, MouseEventArgs e) {

            if (e.Button == MouseButtons.Right) {
                BuildElementTreeNode node = this.buildFileTreeView.GetNodeAt(e.Location) as BuildElementTreeNode;

                if (node != null) {
                    this.SelectBuildElement(node.BuildElement);
                }
            }
        }

    }
}
